﻿using System.Runtime.InteropServices;
using OnDash.Core;
using OnDash.Windowing.GraphicsLibraryFramework.Enums;
using OnDash.Windowing.GraphicsLibraryFramework.Inputs;
using static OnDash.Windowing.GraphicsLibraryFramework.GlfwCallbacks;
using static OnDash.Windowing.GraphicsLibraryFramework.GlfwNative;

namespace OnDash.Windowing.GraphicsLibraryFramework;

public static class Glfw
{
    public const int DontCare = -1;

    public static bool Init() =>
        GlfwNative.Init() == GlfwTrue;

    public static void Terminate() =>
        GlfwNative.Terminate();

    public static void InitHint(InitHintBool hintBool, bool value) =>
        GlfwNative.InitHint((int)hintBool, value ? GlfwTrue : GlfwFalse);

    public static void InitHint(InitHintInt hintInt, int value) =>
        GlfwNative.InitHint((int)hintInt, value);

    public static unsafe void GetVersion(out int major, out int minor, out int revision)
    {
        int localMajor, localMinor, localRevision;
        GlfwNative.GetVersion(&localMajor, &localMinor, &localRevision);
        major = localMajor;
        minor = localMinor;
        revision = localRevision;
    }

    public static unsafe string GetVersionString() =>
        Utils.PtrToStringUtf8(GlfwNative.GetVersionString());

    public static unsafe byte* GetVersionStringRaw() =>
        GlfwNative.GetVersionString();

    public static unsafe ErrorCode GetError(out string description)
    {
        byte* desc;
        var code = GlfwNative.GetError(&desc);
        description = Utils.PtrToStringUtf8(desc);
        return code;
    }

    public static unsafe ErrorCode GetErrorRaw(out byte* description)
    {
        byte* desc;
        var code = GlfwNative.GetError(&desc);
        description = desc;
        return code;
    }

    public static unsafe Monitor** GetMonitorsRaw(out int count)
    {
        fixed (int* ptr = &count) return GlfwNative.GetMonitors(ptr);
    }

    public static unsafe Monitor** GetMonitorsRaw(int* count) =>
        GlfwNative.GetMonitors(count);

    public static unsafe Monitor*[] GetMonitors()
    {
        var ptr = GetMonitorsRaw(out var count);
        if (ptr == null) return null!;

        var array = new Monitor*[count];
        for (var i = 0; i < count; i++) array[i] = ptr[i];

        return array;
    }

    public static unsafe void GetMonitorPos(Monitor* monitor, out int x, out int y)
    {
        int localX, localY;

        GlfwNative.GetMonitorPos(monitor, &localX, &localY);

        x = localX;
        y = localY;
    }

    public static unsafe void GetMonitorPos(Monitor* monitor, int* x, int* y)
    {
        GlfwNative.GetMonitorPos(monitor, x, y);
    }

    public static unsafe void GetMonitorWorkarea(Monitor* monitor, out int x, out int y, out int width, out int height)
    {
        int localX, localY, localWidth, localHeight;

        GlfwNative.GetMonitorWorkarea(monitor, &localX, &localY, &localWidth, &localHeight);

        x = localX;
        y = localY;
        width = localWidth;
        height = localHeight;
    }

    public static unsafe void GetMonitorWorkarea(Monitor* monitor, int* x, int* y, int* width, int* height)
    {
        GlfwNative.GetMonitorWorkarea(monitor, x, y, width, height);
    }

    public static unsafe void GetMonitorPhysicalSize(Monitor* monitor, out int width, out int height)
    {
        int localWidth, localHeight;

        GlfwNative.GetMonitorPhysicalSize(monitor, &localWidth, &localHeight);

        width = localWidth;
        height = localHeight;
    }

    public static unsafe void GetMonitorContentScale(Monitor* monitor, out float scaleX, out float scaleY)
    {
        float localX, localY;

        GlfwNative.GetMonitorContentScale(monitor, &localX, &localY);

        scaleX = localX;
        scaleY = localY;
    }

    public static unsafe void GetMonitorContentScaleRaw(Monitor* monitor, float* scaleX, float* scaleY)
    {
        GlfwNative.GetMonitorContentScale(monitor, scaleX, scaleY);
    }

    public static unsafe string GetMonitorName(Monitor* monitor) =>
        Utils.PtrToStringUtf8(GlfwNative.GetMonitorName(monitor));

    public static unsafe byte* GetMonitorNameRaw(Monitor* monitor) =>
        GlfwNative.GetMonitorName(monitor);

    public static unsafe void SetMonitorUserPointer(Monitor* monitor, void* pointer)
    {
        GlfwNative.SetMonitorUserPointer(monitor, pointer);
    }

    public static unsafe void* GetMonitorUserPointer(Monitor* monitor) =>
        GlfwNative.GetMonitorUserPointer(monitor);

    public static unsafe VideoMode* GetVideoModesRaw(Monitor* monitor, out int count)
    {
        fixed (int* ptr = &count) return GlfwNative.GetVideoModes(monitor, ptr);
    }

    public static unsafe VideoMode* GetVideoModesRaw(Monitor* monitor, int* count) =>
        GlfwNative.GetVideoModes(monitor, count);

    public static unsafe VideoMode[] GetVideoModes(Monitor* monitor)
    {
        var ptr = GetVideoModesRaw(monitor, out var count);
        if (ptr == null) return null!;

        var array = new VideoMode[count];
        for (var i = 0; i < count; i++) array[i] = ptr[i];

        return array;
    }

    public static unsafe void SetGamma(Monitor* monitor, float gamma)
    {
        GlfwNative.SetGamma(monitor, gamma);
    }

    public static unsafe GammaRamp* GetGammaRamp(Monitor* monitor) =>
        GlfwNative.GetGammaRamp(monitor);

    public static unsafe void SetGammaRamp(Monitor* monitor, GammaRamp* ramp)
    {
        GlfwNative.SetGammaRamp(monitor, ramp);
    }

    public static void DefaultWindowHints() =>
        GlfwNative.DefaultWindowHints();

    public static unsafe void WindowHint(WindowHintString hint, string value)
    {
        var ptr = Utils.StringToCoTaskMemUtf8(value);

        try
        {
            WindowHintString((int)hint, (byte*)ptr);
        }
        finally
        {
            Marshal.FreeCoTaskMem(ptr);
        }
    }

    public static unsafe void WindowHintRaw(WindowHintString hint, byte* value)
    {
        WindowHintString((int)hint, value);
    }

    public static unsafe void SetWindowSizeLimits(Window* window, int minWidth, int minHeight, int maxWidth,
        int maxHeight)
    {
        GlfwNative.SetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight);
    }

    public static unsafe void SetWindowAspectRatio(Window* window, int numer, int denom)
    {
        GlfwNative.SetWindowAspectRatio(window, numer, denom);
    }

    public static unsafe void GetWindowFrameSize(Window* window, out int left, out int top, out int right,
        out int bottom)
    {
        int l, t, r, b;
        GlfwNative.GetWindowFrameSize(window, &l, &t, &r, &b);
        left = l;
        top = t;
        right = r;
        bottom = b;
    }

    public static unsafe void GetWindowContentScale(Window* window, out float xScale, out float yScale)
    {
        float x, y;
        GlfwNative.GetWindowContentScale(window, &x, &y);
        xScale = x;
        yScale = y;
    }

    public static unsafe float GetWindowOpacity(Window* window) =>
        GlfwNative.GetWindowOpacity(window);

    public static unsafe void SetWindowOpacity(Window* window, float opacity)
    {
        GlfwNative.SetWindowOpacity(window, opacity);
    }

    public static unsafe void RequestWindowAttention(Window* window) =>
        GlfwNative.RequestWindowAttention(window);

    public static unsafe void SetWindowAttrib(Window* window, WindowAttribute attribute, bool value)
    {
        GlfwNative.SetWindowAttrib(window, attribute, value ? GlfwTrue : GlfwFalse);
    }

    public static bool RawMouseMotionSupported() =>
        GlfwNative.RawMouseMotionSupported() == GlfwTrue;

    public static unsafe string GetKeyName(Keys key, int scanCode) =>
        Utils.PtrToStringUtf8(GlfwNative.GetKeyName(key, scanCode));

    public static unsafe byte* GetKeyNameRaw(Keys key, int scancode) =>
        GlfwNative.GetKeyName(key, scancode);

    public static int GetKeyScancode(Keys key) =>
        GlfwNative.GetKeyScancode(key);

    public static unsafe InputAction GetKey(Window* window, Keys key) =>
        GlfwNative.GetKey(window, key);

    public static unsafe InputAction GetMouseButton(Window* window, MouseButton button) =>
        GlfwNative.GetMouseButton(window, button);

    public static unsafe void GetCursorPos(Window* window, out float posX, out float posY)
    {
        float x, y;
        GlfwNative.GetCursorPos(window, &x, &y);
        posX = x;
        posY = y;
    }

    public static unsafe void GetCursorPosRaw(Window* window, float* xPos, float* yPos)
    {
        GlfwNative.GetCursorPos(window, xPos, yPos);
    }

    public static unsafe void SetCursorPos(Window* window, float posX, float posY)
    {
        GlfwNative.SetCursorPos(window, posX, posY);
    }

    public static unsafe Cursor* CreateCursor(in Image image, int xHot, int yHot)
    {
        fixed (Image* ptr = &image) return CreateCursorRaw(ptr, xHot, yHot);
    }

    public static unsafe Cursor* CreateCursorRaw(Image* image, int xHot, int yHot) =>
        GlfwNative.CreateCursor(image, xHot, yHot);

    public static unsafe Cursor* CreateStandardCursor(CursorShape shape) =>
        GlfwNative.CreateStandardCursor(shape);

    public static unsafe void DestroyCursor(Cursor* cursor) =>
        GlfwNative.DestroyCursor(cursor);

    public static unsafe void SetCursor(Window* window, Cursor* cursor)
    {
        GlfwNative.SetCursor(window, cursor);
    }

    public static bool JoystickPresent(int jId) =>
        GlfwNative.JoystickPresent(jId) == GlfwTrue;

    public static unsafe Span<float> GetJoystickAxes(int jid) =>
        new(GetJoystickAxesRaw(jid, out var count), count);

    public static unsafe float* GetJoystickAxesRaw(int jid, out int count)
    {
        fixed (int* ptr = &count) return GetJoystickAxesRaw(jid, ptr);
    }

    public static unsafe float* GetJoystickAxesRaw(int jId, int* count) =>
        GlfwNative.GetJoystickAxes(jId, count);

    public static unsafe JoystickInputAction[] GetJoystickButtons(int jId)
    {
        var ptr = GetJoystickButtonsRaw(jId, out var count);
        if (ptr == null) return null!;

        var array = new JoystickInputAction[count];
        for (var i = 0; i < count; i++) array[i] = ptr[i];

        return array;
    }

    public static unsafe JoystickInputAction* GetJoystickButtonsRaw(int jId, out int count)
    {
        fixed (int* ptr = &count) return GetJoystickButtonsRaw(jId, ptr);
    }

    public static unsafe JoystickInputAction* GetJoystickButtonsRaw(int jId, int* count) =>
        GlfwNative.GetJoystickButtons(jId, count);

    public static unsafe JoystickHats[] GetJoystickHats(int jId)
    {
        var ptr = GetJoystickHatsRaw(jId, out var count);
        if (ptr == null) return null!;

        var array = new JoystickHats[count];
        for (var i = 0; i < count; i++) array[i] = ptr[i];

        return array;
    }

    public static unsafe JoystickHats* GetJoystickHatsRaw(int jId, out int count)
    {
        fixed (int* ptr = &count) return GlfwNative.GetJoystickHats(jId, ptr);
    }

    public static unsafe JoystickHats* GetJoystickHatsRaw(int jId, int* count) =>
        GlfwNative.GetJoystickHats(jId, count);

    public static unsafe string GetJoystickName(int jId) =>
        Utils.PtrToStringUtf8(GlfwNative.GetJoystickName(jId));

    public static unsafe byte* GetJoystickNameRaw(int jId) =>
        GlfwNative.GetJoystickName(jId);

    public static unsafe string GetJoystickGuid(int jId) =>
        Utils.PtrToStringUtf8(GlfwNative.GetJoystickGuid(jId));

    public static unsafe byte* GetJoystickGuidRaw(int jId) =>
        GlfwNative.GetJoystickGuid(jId);

    public static unsafe void SetJoystickUserPointer(int jId, void* ptr)
    {
        GlfwNative.SetJoystickUserPointer(jId, ptr);
    }

    public static unsafe void* GetJoystickUserPointer(int jId) =>
        GlfwNative.GetJoystickUserPointer(jId);

    public static bool JoystickIsGamepad(int jId) =>
        GlfwNative.JoystickIsGamepad(jId) == GlfwTrue;

    public static unsafe bool UpdateGamepadMappings(string newMapping)
    {
        var ptr = Utils.StringToCoTaskMemUtf8(newMapping);
        try
        {
            return GlfwNative.UpdateGamepadMappings((byte*)ptr) == GlfwTrue;
        }
        finally
        {
            Marshal.FreeCoTaskMem(ptr);
        }
    }

    public static unsafe bool UpdateGamepadMappingsRaw(byte* newMapping) =>
        GlfwNative.UpdateGamepadMappings(newMapping) == GlfwTrue;

    public static unsafe string GetGamepadName(int jId) =>
        Utils.PtrToStringUtf8(GlfwNative.GetGamepadName(jId));

    public static unsafe byte* GetGamepadNameRaw(int jId) =>
        GlfwNative.GetGamepadName(jId);

    public static unsafe bool GetGamepadState(int jId, out GamepadState state)
    {
        fixed (GamepadState* ptr = &state) return GlfwNative.GetGamepadState(jId, ptr) == GlfwTrue;
    }

    public static unsafe bool GetGamepadStateRaw(int jid, GamepadState* state) =>
        GlfwNative.GetGamepadState(jid, state) == GlfwTrue;

    public static double GetTime() =>
        GlfwNative.GetTime();

    public static void SetTime(float time) =>
        GlfwNative.SetTime(time);

    public static long GetTimerValue() =>
        GlfwNative.GetTimerValue();

    public static long GetTimerFrequency() =>
        GlfwNative.GetTimerFrequency();

    public static unsafe Window* GetCurrentContext() =>
        GlfwNative.GetCurrentContext();

    public static unsafe void SwapBuffers(Window* window) =>
        GlfwNative.SwapBuffers(window);

    public static unsafe bool ExtensionSupported(string extensionName)
    {
        var ptr = Utils.StringToCoTaskMemUtf8(extensionName);

        try
        {
            return GlfwNative.ExtensionSupported((byte*)ptr) == GlfwTrue;
        }
        finally
        {
            Marshal.FreeCoTaskMem(ptr);
        }
    }

    public static IntPtr GetProcAddress(string procName) =>
        GlfwNative.GetProcAddress(procName);

    public static unsafe IntPtr GetProcAddressRaw(byte* procName) =>
        GlfwNative.GetProcAddress(procName);

    public static unsafe bool ExtensionSupportedRaw(byte* extensionName) =>
        GlfwNative.ExtensionSupported(extensionName) == GlfwTrue;

    public static unsafe Window* CreateWindow(int width, int height, string title, Monitor* monitor, Window* share)
    {
        var ptr = Utils.StringToCoTaskMemUtf8(title);

        try
        {
            return GlfwNative.CreateWindow(width, height, (byte*)ptr, monitor, share);
        }
        finally
        {
            if (ptr != IntPtr.Zero) Marshal.FreeCoTaskMem(ptr);
        }
    }

    public static unsafe Window* CreateWindowRaw(int width, int height, byte* title, Monitor* monitor, Window* share) =>
        GlfwNative.CreateWindow(width, height, title, monitor, share);

    public static unsafe void DestroyWindow(Window* window) =>
        GlfwNative.DestroyWindow(window);

    public static unsafe void FocusWindow(Window* window) =>
        GlfwNative.FocusWindow(window);

    public static unsafe string GetClipboardString(Window* window) =>
        Utils.PtrToStringUtf8(GlfwNative.GetClipboardString(window));

    public static unsafe byte* GetClipboardStringRaw(Window* window) =>
        GlfwNative.GetClipboardString(window);

    public static unsafe void GetFramebufferSize(Window* window, out int width, out int height)
    {
        int w, h;
        GlfwNative.GetFramebufferSize(window, &w, &h);
        width = w;
        height = h;
    }

    public static unsafe void GetFramebufferSizeRaw(Window* window, int* width, int* height)
    {
        GlfwNative.GetFramebufferSize(window, width, height);
    }

    public static unsafe bool GetInputMode(Window* window, StickyAttributes mode) =>
        GlfwNative.GetInputMode(window, mode) == GlfwTrue;

    public static unsafe CursorModeValue GetInputMode(Window* window, CursorStateAttribute mode) =>
        GlfwNative.GetInputMode(window, mode);

    public static unsafe bool GetInputMode(Window* window, LockKeyModAttribute mode) =>
        GlfwNative.GetInputMode(window, mode) == GlfwTrue;

    public static unsafe bool GetInputMode(Window* window, RawMouseMotionAttribute mode) =>
        GlfwNative.GetInputMode(window, mode) == GlfwTrue;

    public static unsafe Monitor* GetPrimaryMonitor() =>
        GlfwNative.GetPrimaryMonitor();

    public static unsafe VideoMode* GetVideoMode(Monitor* monitor) =>
        GlfwNative.GetVideoMode(monitor);

    public static unsafe bool GetWindowAttrib(Window* window, WindowAttributeGetBool attribute) =>
        GlfwNative.GetWindowAttrib(window, attribute) == GlfwTrue;

    public static unsafe int GetWindowAttrib(Window* window, WindowAttributeGetInt attribute) =>
        GlfwNative.GetWindowAttrib(window, attribute);

    public static unsafe ClientApi GetWindowAttrib(Window* window, WindowAttributeGetClientApi attribute) =>
        GlfwNative.GetWindowAttrib(window, attribute);

    public static unsafe ContextApi GetWindowAttrib(Window* window, WindowAttributeGetContextApi attribute) =>
        GlfwNative.GetWindowAttrib(window, attribute);

    public static unsafe OpenGlProfile GetWindowAttrib(Window* window, WindowAttributeGetOpenGlProfile attribute) =>
        GlfwNative.GetWindowAttrib(window, attribute);

    public static unsafe ReleaseBehavior GetWindowAttrib(Window* window, WindowAttributeGetReleaseBehavior attribute) =>
        GlfwNative.GetWindowAttrib(window, attribute);

    public static unsafe Robustness GetWindowAttrib(Window* window, WindowAttributeGetRobustness attribute) =>
        GlfwNative.GetWindowAttrib(window, attribute);

    public static unsafe void SetWindowUserPointer(Window* window, void* pointer)
    {
        GlfwNative.SetWindowUserPointer(window, pointer);
    }

    public static unsafe void* GetWindowUserPointer(Window* window) =>
        GlfwNative.GetWindowUserPointer(window);

    public static unsafe void GetWindowSize(Window* window, out int width, out int height)
    {
        int w, h;
        GlfwNative.GetWindowSize(window, &w, &h);
        width = w;
        height = h;
    }

    public static unsafe void GetWindowSizeRaw(Window* window, int* width, int* height)
    {
        GlfwNative.GetWindowSize(window, width, height);
    }

    public static unsafe void GetWindowPos(Window* window, out int x, out int y)
    {
        int lX, lY;
        GlfwNative.GetWindowPos(window, &lX, &lY);
        x = lX;
        y = lY;
    }

    public static unsafe void GetWindowPosRaw(Window* window, int* x, int* y)
    {
        GlfwNative.GetWindowPos(window, x, y);
    }

    public static unsafe Monitor* GetWindowMonitor(Window* window) =>
        GlfwNative.GetWindowMonitor(window);

    public static unsafe void HideWindow(Window* window) =>
        GlfwNative.HideWindow(window);

    public static unsafe void IconifyWindow(Window* window) =>
        GlfwNative.IconifyWindow(window);

    public static unsafe void MakeContextCurrent(Window* window) =>
        GlfwNative.MakeContextCurrent(window);

    public static unsafe void MaximizeWindow(Window* window) =>
        GlfwNative.MaximizeWindow(window);

    public static unsafe IntPtr SetWindowMaximizeCallback(Window* window, WindowMaximizeCallback callback) =>
        GlfwNative.SetWindowMaximizeCallback(window, callback);

    public static unsafe IntPtr SetFramebufferSizeCallback(Window* window, FramebufferSizeCallback callback) =>
        GlfwNative.SetFramebufferSizeCallback(window, callback);

    public static unsafe IntPtr SetWindowContentScaleCallback(Window* window, WindowContentScaleCallback callback) =>
        GlfwNative.SetWindowContentScaleCallback(window, callback);

    public static void PollEvents() =>
        GlfwNative.PollEvents();

    public static void PostEmptyEvent() =>
        GlfwNative.PostEmptyEvent();

    public static unsafe void RestoreWindow(Window* window) =>
        GlfwNative.RestoreWindow(window);

    public static unsafe IntPtr SetCharCallback(Window* window, CharCallback callback) =>
        GlfwNative.SetCharCallback(window, callback);

    public static unsafe IntPtr SetCharModsCallback(Window* window, CharModsCallback callback) =>
        GlfwNative.SetCharModsCallback(window, callback);

    public static unsafe void SetClipboardString(Window* window, string data)
    {
        var ptr = Utils.StringToCoTaskMemUtf8(data);

        try
        {
            GlfwNative.SetClipboardString(window, (byte*)ptr);
        }
        finally
        {
            Marshal.FreeCoTaskMem(ptr);
        }
    }

    public static unsafe void SetClipboardStringRaw(Window* window, byte* data)
    {
        GlfwNative.SetClipboardString(window, data);
    }

    public static unsafe IntPtr SetCursorEnterCallback(Window* window, CursorEnterCallback callback) =>
        GlfwNative.SetCursorEnterCallback(window, callback);

    public static unsafe IntPtr SetCursorPosCallback(Window* window, CursorPosCallback callback) =>
        GlfwNative.SetCursorPosCallback(window, callback);

    public static unsafe IntPtr SetDropCallback(Window* window, DropCallback callback) =>
        GlfwNative.SetDropCallback(window, callback);

    public static IntPtr SetErrorCallback(ErrorCallback callback) =>
        GlfwNative.SetErrorCallback(callback);

    public static unsafe void SetInputMode(Window* window, CursorStateAttribute mode, CursorModeValue value)
    {
        GlfwNative.SetInputMode(window, mode, value);
    }

    public static unsafe void SetInputMode(Window* window, StickyAttributes mode, bool value)
    {
        GlfwNative.SetInputMode(window, mode, value ? GlfwTrue : GlfwFalse);
    }

    public static unsafe void SetInputMode(Window* window, LockKeyModAttribute mode, bool value)
    {
        GlfwNative.SetInputMode(window, mode, value ? GlfwTrue : GlfwFalse);
    }

    public static unsafe void SetInputMode(Window* window, RawMouseMotionAttribute mode, bool value)
    {
        GlfwNative.SetInputMode(window, mode, value ? GlfwTrue : GlfwFalse);
    }

    public static IntPtr SetJoystickCallback(JoystickCallback callback) =>
        GlfwNative.SetJoystickCallback(callback);

    public static unsafe IntPtr SetKeyCallback(Window* window, KeyCallback callback) =>
        GlfwNative.SetKeyCallback(window, callback);

    public static unsafe IntPtr SetScrollCallback(Window* window, ScrollCallback callback) =>
        GlfwNative.SetScrollCallback(window, callback);

    public static IntPtr SetMonitorCallback(MonitorCallback callback) =>
        GlfwNative.SetMonitorCallback(callback);

    public static unsafe IntPtr SetMouseButtonCallback(Window* window, MouseButtonCallback callback) =>
        GlfwNative.SetMouseButtonCallback(window, callback);

    public static unsafe IntPtr SetWindowCloseCallback(Window* window, WindowCloseCallback callback) =>
        GlfwNative.SetWindowCloseCallback(window, callback);

    public static unsafe IntPtr SetWindowFocusCallback(Window* window, WindowFocusCallback callback) =>
        GlfwNative.SetWindowFocusCallback(window, callback);

    public static unsafe void SetWindowIcon(Window* window, ReadOnlySpan<Image> images)
    {
        fixed (Image* ptr = images) GlfwNative.SetWindowIcon(window, images.Length, ptr);
    }

    public static unsafe void SetWindowIconRaw(Window* window, int count, Image* images)
    {
        GlfwNative.SetWindowIcon(window, count, images);
    }

    public static unsafe IntPtr SetWindowIconifyCallback(Window* window, WindowIconifyCallback callback) =>
        GlfwNative.SetWindowIconifyCallback(window, callback);

    public static unsafe void SetWindowMonitor(Window* window, Monitor* monitor, int x, int y, int width, int height,
        int refreshRate)
    {
        GlfwNative.SetWindowMonitor(window, monitor, x, y, width, height, refreshRate);
    }

    public static unsafe void SetWindowPos(Window* window, int x, int y)
    {
        GlfwNative.SetWindowPos(window, x, y);
    }

    public static unsafe IntPtr SetWindowPosCallback(Window* window, WindowPosCallback callback) =>
        GlfwNative.SetWindowPosCallback(window, callback);

    public static unsafe IntPtr SetWindowRefreshCallback(Window* window, WindowRefreshCallback callback) =>
        GlfwNative.SetWindowRefreshCallback(window, callback);

    public static unsafe void SetWindowSize(Window* window, int width, int height)
    {
        GlfwNative.SetWindowSize(window, width, height);
    }

    public static unsafe IntPtr SetWindowSizeCallback(Window* window, WindowSizeCallback callback) =>
        GlfwNative.SetWindowSizeCallback(window, callback);

    public static unsafe void SetWindowShouldClose(Window* window, bool value)
    {
        GlfwNative.SetWindowShouldClose(window, value ? GlfwTrue : GlfwFalse);
    }

    public static unsafe void SetWindowTitle(Window* window, string title)
    {
        var ptr = Utils.StringToCoTaskMemUtf8(title);

        try
        {
            GlfwNative.SetWindowTitle(window, (byte*)ptr);
        }
        finally
        {
            Marshal.FreeCoTaskMem(ptr);
        }
    }

    public static unsafe void SetWindowTitleRaw(Window* window, byte* title)
    {
        GlfwNative.SetWindowTitle(window, title);
    }

    public static unsafe void ShowWindow(Window* window) =>
        GlfwNative.ShowWindow(window);

    public static void SwapInterval(int interval) =>
        GlfwNative.SwapInterval(interval);

    public static void WaitEvents() =>
        GlfwNative.WaitEvents();

    public static void WaitEventsTimeout(double timeout) =>
        GlfwNative.WaitEventsTimeout(timeout);

    public static void WindowHint(WindowHintInt hint, int value) =>
        GlfwNative.WindowHint(hint, value);

    public static void WindowHint(WindowHintBool hint, bool value)
    {
        GlfwNative.WindowHint(hint, value ? GlfwTrue : GlfwFalse);
    }

    public static void WindowHint(WindowHintClientApi hint, ClientApi value) =>
        GlfwNative.WindowHint(hint, value);

    public static void WindowHint(WindowHintReleaseBehavior hint, ReleaseBehavior value)
    {
        GlfwNative.WindowHint(hint, value);
    }

    public static void WindowHint(WindowHintContextApi hint, ContextApi value)
    {
        GlfwNative.WindowHint(hint, value);
    }

    public static void WindowHint(WindowHintRobustness hint, Robustness value)
    {
        GlfwNative.WindowHint(hint, value);
    }

    public static void WindowHint(WindowHintOpenGlProfile hint, OpenGlProfile value)
    {
        GlfwNative.WindowHint(hint, value);
    }

    public static unsafe bool WindowShouldClose(Window* window) =>
        GlfwNative.WindowShouldClose(window) == GlfwTrue;

    public static bool VulkanSupported() =>
        GlfwNative.VulkanSupported() == GlfwTrue;

    public static unsafe byte** GetRequiredInstanceExtensionsRaw(out uint count)
    {
        fixed (uint* ptr = &count) return GlfwNative.GetRequiredInstanceExtensions(ptr);
    }

    public static unsafe byte** GetRequiredInstanceExtensionsRaw(uint* count) =>
        GlfwNative.GetRequiredInstanceExtensions(count);

    public static unsafe string[] GetRequiredInstanceExtensions()
    {
        var ptr = GetRequiredInstanceExtensionsRaw(out var count);

        var array = new string[count];
        for (var i = 0; i < count; i++) array[i] = Utils.PtrToStringUtf8(ptr[i]);
        return array;
    }

    public static unsafe IntPtr GetInstanceProcAddress(VkHandle instance, string procName)
    {
        var ptr = Utils.StringToCoTaskMemUtf8(procName);

        try
        {
            return GlfwNative.GetInstanceProcAddress(instance, (byte*)ptr);
        }
        finally
        {
            Marshal.FreeCoTaskMem(ptr);
        }
    }

    public static unsafe IntPtr GetInstanceProcAddressRaw(VkHandle instance, byte* procName) =>
        GlfwNative.GetInstanceProcAddress(instance, procName);

    public static bool GetPhysicalDevicePresentationSupport(VkHandle instance, VkHandle device, int queueFamily) =>
        GlfwNative.GetPhysicalDevicePresentationSupport(instance, device, queueFamily) == GlfwTrue;

    public static unsafe int CreateWindowSurface(VkHandle instance, Window* window, void* allocator,
        out VkHandle surface) => GlfwNative.CreateWindowSurface(instance, window, allocator, out surface);

    public static unsafe string GetWin32Adapter(Monitor* monitor)
    {
        var strPtr = GlfwNative.GetWin32Adapter(monitor);
        var str = Marshal.PtrToStringUTF8((IntPtr)strPtr)!;
        return str;
    }

    public static unsafe string GetWin32Monitor(Monitor* monitor)
    {
        var strPtr = GlfwNative.GetWin32Monitor(monitor);
        var str = Marshal.PtrToStringUTF8((IntPtr)strPtr)!;
        return str;
    }

    public static unsafe IntPtr GetWin32Window(Window* window) =>
        GlfwNative.GetWin32Window(window);

    public static unsafe IntPtr GetWglContext(Window* window) =>
        GlfwNative.GetWglContext(window);

    public static unsafe uint GetCocoaMonitor(Monitor* monitor) =>
        GlfwNative.GetCocoaMonitor(monitor);

    public static unsafe IntPtr GetCocoaWindow(Window* window) =>
        GlfwNative.GetCocoaWindow(window);

    public static unsafe IntPtr GetNsGlContext(Window* window) =>
        GlfwNative.GetNsGlContext(window);

    public static IntPtr GetX11Display() =>
        GlfwNative.GetX11Display();

    public static unsafe UIntPtr GetX11Adapter(Monitor* monitor) =>
        GlfwNative.GetX11Adapter(monitor);

    public static unsafe UIntPtr GetX11Monitor(Monitor* monitor) =>
        GlfwNative.GetX11Monitor(monitor);

    public static unsafe UIntPtr GetX11Window(Window* window) =>
        GlfwNative.GetX11Window(window);

    public static unsafe void SetX11SelectionString(string @string)
    {
        var strPtr = Marshal.StringToCoTaskMemUTF8(@string);
        GlfwNative.SetX11SelectionString((byte*)strPtr);
        Marshal.FreeCoTaskMem(strPtr);
    }

    public static unsafe string GetX11SelectionString()
    {
        var strPtr = GlfwNative.GetX11SelectionString();
        return Marshal.PtrToStringUTF8((IntPtr)strPtr)!;
    }

    public static unsafe uint GetGlXContext(Window* window) =>
        GlfwNative.GetGlXContext(window);

    public static unsafe uint GetGlXWindow(Window* window) =>
        GlfwNative.GetGlXWindow(window);

    public static IntPtr GetWaylandDisplay() =>
        GlfwNative.GetWaylandDisplay();

    public static unsafe IntPtr GetWaylandMonitor(Monitor* monitor) =>
        GlfwNative.GetWaylandMonitor(monitor);

    public static unsafe IntPtr GetWaylandWindow(Window* window) =>
        GlfwNative.GetWaylandWindow(window);

    public static IntPtr GetEglDisplay() =>
        GlfwNative.GetEGLDisplay();

    public static unsafe IntPtr GetEglContext(Window* window) =>
        GlfwNative.GetEglContext(window);

    public static unsafe IntPtr GetEglSurface(Window* window) =>
        GlfwNative.GetEglSurface(window);

    public static unsafe bool GetOsMesaColorBuffer(Window* window, out int width, out int height, out int format,
        out IntPtr buffer)
    {
        fixed (int* widthPtr = &width)
        fixed (int* heightPtr = &height)
        fixed (int* formatPtr = &format)
        fixed (IntPtr* bufferPtr = &buffer)
            return GetOSMesaColorBuffer(window, widthPtr, heightPtr, formatPtr, (void**)bufferPtr) != 0;
    }

    public static unsafe bool GetOsMesaDepthBuffer(Window* window, out int width, out int height, out int bytesPerValue,
        out IntPtr buffer)
    {
        fixed (int* widthPtr = &width)
        fixed (int* heightPtr = &height)
        fixed (int* bytesPerValuePtr = &bytesPerValue)
        fixed (IntPtr* bufferPtr = &buffer)
        {
            return GetOSMesaDepthBuffer(window, widthPtr, heightPtr, bytesPerValuePtr, (void**)bufferPtr) != 0;
        }
    }

    public static unsafe IntPtr GetOsMesaContext(Window* window) =>
        GlfwNative.GetOSMesaContext(window);
}